﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using util;
using Newtonsoft.Json;
using System.IO;

namespace 文件管理
{
    public class SocketClient
    {
        public static short CMD_STR = 0X0A01;

        public static short CMD_HEART = 0X00F1;

        //发文件，传文件名也可能带路径
        public static short CMD_SEND_FILE = 0X00F6;
        //指请求远端文件列表
        public static short CMD_REQ_FILE_LIST = 0X0AF1;
        //发送文件列表
        public static short CMD_SEND_FILE_LIST = 0X0AF2;
        //请求指向性文件
        public static short CMD_REQ_FILE = 0X0AF3;

        //推送文件到对方指定位置
        public static short CMD_SET_FILE = 0X0AF4;
        //删除指定文件
        public static short CMD_DELETE_FILE = 0X0AF5;

        Socket socket;
        private Thread readThread;
        private Thread writeThread;
        public bool isRunning = false;

        SocketListener listener;

        string current_path;
        private static readonly object lockObject = new object();

        public SocketClient() { }

        public void setListener(SocketListener l)
        {
            listener = l;
        }

        public bool connect(string host) {
            try
            {
                FileLogger.Debug("connect = " + host);
                int port = 65432;
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket.Connect(new IPEndPoint(IPAddress.Parse(host), port));
                isRunning = true;
                readThread = new Thread(new ThreadStart(Read));

                readThread.Start();
            }
            catch (ArgumentNullException e)
            {
                Console.WriteLine("ArgumentNullException : {0}", e.Message);
            }
            catch (SocketException e)
            {
                Console.WriteLine("SocketException : {0}", e.Message);
            }
            return isRunning;
        }


        private void Read()
        {
            while (isRunning)
            {
                try
                {
                    byte[] buffer = Util.ReceiveExact(socket,2);
                    if (buffer == null) // 连接已关闭
                    {
                        break;
                    }
                    int receivedValue = buffer[0] << 8 | buffer[1];
                    //Console.WriteLine("receivedValue = " + receivedValue);
                    if (receivedValue == CMD_HEART)
                    {
                       // Console.WriteLine("heart beat!!");
                    } else if (receivedValue == CMD_STR)
                    {
                        buffer = Util.ReceiveExact(socket, 4);
                        int len = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
                        Console.WriteLine("str len = " + len);
                        buffer = Util.ReceiveExact(socket, len);
                        string text = Encoding.UTF8.GetString(buffer);
                        Console.WriteLine($"Received: {text}");
                    }
                    else if (receivedValue == CMD_SEND_FILE_LIST)
                    {
                        buffer = Util.ReceiveExact(socket, 4);
                        int len = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
                        buffer = Util.ReceiveExact(socket, len);
                        string text = Encoding.UTF8.GetString(buffer);
                        Console.WriteLine($"Received: {text}");
                        DirectoryBean d = JsonConvert.DeserializeObject<DirectoryBean>(text);
                        if (listener != null)
                        {
                            listener.OnFileList(d);
                        }
                    }
                    else if ( receivedValue == CMD_SEND_FILE)
                    {
                        buffer = Util.ReceiveExact(socket, 4);
                        int len = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
                        buffer = Util.ReceiveExact(socket, len);
                        string text = Encoding.UTF8.GetString(buffer);

                        buffer = Util.ReceiveExact(socket, 8);
                        long len1 = Util.getLong(buffer);
                        string fileName = Path.GetFileName(text);
                        string path =  fileName;
                        bool s = Util.ReceiveAndSaveData(socket, len1, path,r =>
                        {
                            if (listener != null)
                            {
                                listener.OnPercent(r);
                            }
                        });
                        if (listener != null)
                        {
                            listener.OnDownloadResult(s,s?"成功:" + path : "下载失败");
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Read error: {ex.Message}");
                    isRunning = false;
                    if (listener != null)
                    {
                        listener.onSocketClose();
                    }
                   
                    break;
                }
            }

        }

        public void deleteFile(string path)
        {
            Console.WriteLine("deleteFile path = " + path);
            byte[] cmd = new byte[] { 0x0A, 0xF5 };

            byte[] asciiByteArray = Encoding.UTF8.GetBytes(path);
            int length = asciiByteArray.Length;
            byte[] newArray = new byte[length + 6];
            newArray[0] = cmd[0];
            newArray[1] = cmd[1];

            newArray[2] = (byte)((length >> 24) & 0xFF);
            newArray[3] = (byte)((length >> 16) & 0xFF);
            newArray[4] = (byte)((length >> 8) & 0xFF);
            newArray[5] = (byte)((length) & 0xFF);

            Array.Copy(asciiByteArray, 0, newArray, 6, length);
            Thread thread = new Thread(new ParameterizedThreadStart(Write));
            thread.Start(newArray);
            reqFileList(current_path);
        }


        public string GetCurrentPath()
        {
            return current_path;
        }


        public void send(string msg)
        {

            byte[] asciiByteArray = Encoding.UTF8.GetBytes(msg);
            int length = asciiByteArray.Length;
            byte[] newArray = new byte[length + 6];
            newArray[0] = 0x0A;
            newArray[1] = 0x01;

            newArray[2] = (byte)((length >> 24) & 0xFF);
            newArray[3] = (byte)((length >> 16) & 0xFF);
            newArray[4] = (byte)((length >> 8) & 0xFF);
            newArray[5] = (byte)((length) & 0xFF);

            // 将原始数组的内容复制到新数组的其余位置
            Array.Copy(asciiByteArray, 0, newArray, 6, length);
            Thread thread = new Thread(new ParameterizedThreadStart(Write));
            thread.Start(newArray);
        }

        public void reqFileList(string path)
        {
            Console.WriteLine("reqFileList path = " + path);
            current_path = path;
            byte[] cmd = new byte[] { 0x0A, 0xF1 };

            byte[] asciiByteArray = Encoding.UTF8.GetBytes(path);
            int length = asciiByteArray.Length;
            byte[] newArray = new byte[length + 6];
            newArray[0] = cmd[0];
            newArray[1] = cmd[1];

            newArray[2] = (byte)((length >> 24) & 0xFF);
            newArray[3] = (byte)((length >> 16) & 0xFF);
            newArray[4] = (byte)((length >> 8) & 0xFF);
            newArray[5] = (byte)((length) & 0xFF);

            Array.Copy(asciiByteArray, 0, newArray, 6, length);
            Thread thread = new Thread(new ParameterizedThreadStart(Write));
            thread.Start(newArray);
        }

        public void reqFile(string path)
        {
            Console.WriteLine("reqFile path = " + path);
            byte[] cmd = new byte[] { 0x0A, 0xF3 };

            byte[] asciiByteArray = Encoding.UTF8.GetBytes(path);
            int length = asciiByteArray.Length;
            byte[] newArray = new byte[length + 6];
            newArray[0] = cmd[0];
            newArray[1] = cmd[1];

            newArray[2] = (byte)((length >> 24) & 0xFF);
            newArray[3] = (byte)((length >> 16) & 0xFF);
            newArray[4] = (byte)((length >> 8) & 0xFF);
            newArray[5] = (byte)((length) & 0xFF);

            Array.Copy(asciiByteArray, 0, newArray, 6, length);
            Thread thread = new Thread(new ParameterizedThreadStart(Write));
            thread.Start(newArray);
        }

        public void sendFile(string path)
        {
            Thread thread = new Thread(new ParameterizedThreadStart(doSendFile));
            thread.Start((path, false));
            //doSendFile((path, false));
        }

        public void sendFileToPath(string path,string relativePath)
        {
            Thread thread = new Thread(new ParameterizedThreadStart(doSendFile));
            thread.Start((path, true, relativePath));
        }

        public void sendFilesToPath(string[] files)
        {
            Thread thread = new Thread(new ParameterizedThreadStart(doSendFiles));
            thread.Start(files);
        }

        private void doSendFiles(object files1)
        {
            string[] files = files1 as string[];
            foreach (string file in files)
            {
                Console.WriteLine(file);
                if (File.Exists(file))
                {
                    Console.WriteLine($"{file} is a file.");
                    doSendFile((file,true,Path.GetFileName(file)));
                }
                else if (Directory.Exists(file))
                {
                    Console.WriteLine($"{file} is a folder.");
                    Console.WriteLine(Path.GetFileName(file));
                    Util.TraverseAndSendFiles(file, Path.GetDirectoryName(file), this);
                }
                else
                {
                    Console.WriteLine($"{file} does not exist.");
                }
            }
            reqFileList(current_path);
        }

        public void doSendFile(object param)
        {
            string cPath = current_path;

            string rpath = null;
            string filePath = null;
            bool setfile = false;
            if (param is ValueTuple<string, bool> tupleTwo)
            {
                (filePath, setfile) = tupleTwo;
                Console.WriteLine($"File path: {filePath}, Set file: {setfile}");
            }
            else if (param is ValueTuple<string, bool, string> tupleThree)
            {
                (filePath, setfile, rpath) = tupleThree;
            }
            bool s = false;

            lock (lockObject)
            {
                if (!setfile)
            {
                s = Util.SendFile(socket, filePath, r =>
                {
                    if (listener != null)
                    {
                        listener.OnPercent(r);
                    }
                });
            } else
            {
                string remotePath = cPath + "/" + rpath;
                Console.WriteLine("remotePath = " + remotePath);
                s = Util.SendFileToPath(socket, filePath, remotePath, r =>
                {
                    if (listener != null)
                    {
                        listener.OnPercent(r);
                    }
                });
            }
            if (listener != null)
            {
                if (!setfile)
                {
                    listener.OnDownloadResult(s, s ? "保存在Download/share/" + Path.GetFileName(filePath) : "上传失败");
                }
                else
                {
                    listener.OnDownloadResult(s, s ? "保存在" + cPath + "目录下" : "上传失败");
                }
                if (s)
                {
                   // reqFileList(current_path);
                }
            }
            }
        }

        private void Write(object parameter)
        {
            byte[] data = (byte[])parameter;
            lock (lockObject)
            {
            try
            {
                socket.Send(data);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Write error: {ex.Message}");
            }
            }
        }

        public void Stop()
        {
            isRunning = false;
            if (socket == null)
            {
                return;
            }
            try
            {
                socket.Shutdown(SocketShutdown.Both);
                socket.Close();
            } catch (Exception ex)
            {

            }

            if (readThread != null && readThread.IsAlive)
            {
                readThread.Join();
            }
            if (writeThread != null && writeThread.IsAlive)
            {
                writeThread.Join();
            }
            socket = null;
            //listener = null;
        }
    }

    public interface SocketListener
    {
       void OnFileList(DirectoryBean b);

        void onSocketClose();

        void OnPercent(int percent);

        void OnDownloadResult(bool success,string msg);
    }

 
}
